package com.sinosoft.hydra.um.services;

import com.sinosoft.hydra.um.constant.BaseConstant;
import com.sinosoft.hydra.um.domain.model.LabCondition;
import com.sinosoft.hydra.um.domain.permission.TProcessParameter;
import com.sinosoft.hydra.um.domain.permission.TProcessRecord;
import com.sinosoft.hydra.um.repositories.TProcessRecordDAO;
import cn.com.sinux.spring.utils.DateUtils;
import cn.com.sinux.spring.utils.QueryHelper;
import cn.com.sinux.spring.utils.SpecificationHelper;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateFormatUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.math.BigDecimal;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * Created by Administrator on 2016/7/12.
 */
@Service
public class SysRecordMatchService {

    @Autowired
    private TProcessRecordDAO processRecordDAO;

    private LabCondition getLabCondition(List<LabCondition> labConditions, String templateId) {
        for (LabCondition labCondition : labConditions) {
            if (labCondition.getId().equals(templateId)) {
                return labCondition;
            }
        }
        return null;
    }

    private static final String OPERATOR = "operator";
    private static final String OPERATE_TIME = "operateTime";
    private static final String OPERATE_RESULT = "result";
    private static final String OPERATE_OTHERS = "others";

    private boolean matchContains(String src, String dest) {
        return !StringUtils.isBlank(src) && (StringUtils.isBlank(dest) || src.contains(dest));
    }

    private boolean matchEqual(String src, String dest) {
        if (StringUtils.isNotEmpty(src)) {
            return src.equals(dest);
        }
        return StringUtils.isEmpty(dest);
    }

    private boolean matchNumberLess(BigDecimal src, BigDecimal dest) {
        if (src == null) {
            src = BigDecimal.ZERO;
        }
        if (dest == null) {
            dest = BigDecimal.ZERO;
        }
        return src.compareTo(dest) < 0;
    }

    private boolean matchNumberMore(BigDecimal src, BigDecimal dest) {
        return matchNumberLess(dest, src);
    }

    private boolean matchNumberLessAndEqual(BigDecimal src, BigDecimal dest) {
        if (src == null) {
            src = BigDecimal.ZERO;
        }
        if (dest == null) {
            dest = BigDecimal.ZERO;
        }
        return src.compareTo(dest) <= 0;
    }

    private boolean matchNumberMoreAndEqual(BigDecimal src, BigDecimal dest) {
        return matchNumberLessAndEqual(dest, src);
    }

    private boolean matchNumberBetween(BigDecimal src, BigDecimal dest1, BigDecimal dest2) {
        return matchNumberLessAndEqual(src, dest2) && matchNumberMoreAndEqual(src, dest1);
    }

    private boolean matchDateLess(Date src, Date dest) {
        if (src == null) {
            src = new Date();
        }
        if (dest == null) {
            dest = new Date();
        }
        return src.compareTo(dest) < 0;
    }

    private boolean matchDateLessAndEqual(Date src, Date dest) {
        return src.compareTo(dest) <= 0;
    }

    private boolean matchDateMore(Date src, Date dest) {
        if (src == null) {
            src = new Date();
        }
        if (dest == null) {
            dest = new Date();
        }
        return matchDateLess(dest, src);
    }

    private boolean matchDateMoreAndEqual(Date src, Date dest) {
        return matchDateLess(dest, src);
    }

    private boolean matchDateBetween(Date src, Date dest1, Date dest2) {
        return matchDateLessAndEqual(src, dest2) && matchDateMoreAndEqual(src, dest1);
    }

    private final static String[] patterns = new String[]{"yyyy-MM-dd hh:mm:ss"};

    private Date parse(String src) {
        try {
            return DateUtils.parseDate(src, patterns);
        } catch (ParseException e) {
            return new Date();
        }
    }

    private boolean match(String attrCondition, String filedType, String src, String dest) {
        if ("contain".equals(attrCondition)) {
            return matchContains(src, dest);
        } else if ("equal".equals(attrCondition)) {
            return matchEqual(src, dest);
        } else if ("less".equals(attrCondition)) {
            return BaseConstant.FiledType.FIELD_TYPE_NUM.name().equals(filedType)
                    ? matchNumberLess(BigDecimal.valueOf(Double.valueOf(src)), BigDecimal.valueOf(Double.valueOf(dest)))
                    : matchDateLess(parse(src), parse(dest));
        } else if ("lessOrEqual".equals(attrCondition)) {
            return BaseConstant.FiledType.FIELD_TYPE_NUM.name().equals(filedType)
                    ? matchNumberLessAndEqual(BigDecimal.valueOf(Double.valueOf(src)), BigDecimal.valueOf(Double.valueOf(dest)))
                    : matchDateLessAndEqual(parse(src), parse(dest));
        } else if ("between".equals(attrCondition)) {
            String[] dests = dest.split(",");
            return BaseConstant.FiledType.FIELD_TYPE_NUM.name().equals(filedType)
                    ? matchNumberBetween(BigDecimal.valueOf(Double.valueOf(src)), BigDecimal.valueOf(Double.valueOf(dests[0])), BigDecimal.valueOf(Double.valueOf(dests[1])))
                    : matchDateBetween(parse(src), parse(dests[0]), parse(dests[1]));
        } else if ("more".equals(attrCondition)) {
            return BaseConstant.FiledType.FIELD_TYPE_NUM.name().equals(filedType)
                    ? matchNumberMore(BigDecimal.valueOf(Double.valueOf(src)), BigDecimal.valueOf(Double.valueOf(dest)))
                    : matchDateMore(parse(src), parse(dest));
        } else if ("moreOrEqual".equals(attrCondition)) {
            return BaseConstant.FiledType.FIELD_TYPE_NUM.name().equals(filedType)
                    ? matchNumberMoreAndEqual(BigDecimal.valueOf(Double.valueOf(src)), BigDecimal.valueOf(Double.valueOf(dest)))
                    : matchDateMoreAndEqual(parse(src), parse(dest));
        }
        return false;
    }

    private TProcessParameter getParameter(List<TProcessParameter> processParameters, String filedName) {
        for (TProcessParameter processParameter : processParameters) {
            if (filedName.equals(processParameter.getFieldName())) {
                return processParameter;
            }
        }
        return null;
    }

    public List<TProcessRecord> findByLabConditions(List<LabCondition> labConditions) {
        //首先根据labconditon的id找到所有template
        final List<String> ids = QueryHelper.doQueryProperties(labConditions, true, new QueryHelper.PropertyReader<LabCondition, String>() {

            public String read(LabCondition labCondition) {
                return labCondition.getId();
            }
        });
        List<TProcessRecord> processRecords = processRecordDAO.findAll(SpecificationHelper.buildSpecification(new SpecificationHelper.SpecificationBuilder<TProcessRecord>() {

            public void fillParam(Root<TProcessRecord> root, CriteriaBuilder cb, List<Predicate> list) {
                list.add(root.get("tProcessTemplate").get("id").as(String.class).in(ids));
            }
        }));
        List<TProcessRecord> matches = new ArrayList<TProcessRecord>();
        for (TProcessRecord processRecord : processRecords) {
            LabCondition labCondition = getLabCondition(labConditions, processRecord.gettProcessTemplate().getId());
            List<LabCondition.Condition> conditions = labCondition.getConditions();
            boolean matched = true;
            for (LabCondition.Condition condition : conditions) {
                if (OPERATOR.equals(condition.getAttrType())) {
                    matched = match(condition.getAttrCondition(), condition.getAttrFieldType(), processRecord.getOperator(), condition.getAttrValue());
                } else if (OPERATE_TIME.equals(condition.getAttrType())) {
                    matched = match(condition.getAttrCondition(), condition.getAttrFieldType(), DateFormatUtils.format(processRecord.getOperateTime(),
                            "yyyy-MM-dd HH:mm:ss"), condition.getAttrValue());
                } else if (OPERATE_RESULT.equals(condition.getAttrType())) {
                    matched = match(condition.getAttrCondition(), condition.getAttrFieldType(), processRecord.getResult(), condition.getAttrValue());
                } else if (OPERATE_OTHERS.equals(condition.getAttrType())) {
                    TProcessParameter processParameter = getParameter(processRecord.gettProcessParameters(), condition.getAttrName());
                    matched = processParameter != null && match(condition.getAttrCondition(), condition.getAttrFieldType(),
                            processParameter.getParameterValue(), condition.getAttrValue());
                }
                if (!matched) {
                    break;
                }
            }
            if (matched) {
                matches.add(processRecord);
            }
        }
        //根据条件进行过滤
        return matches;
    }
}
